import { LifeCycles, cached, isInterval, roomNameToXY } from '@/ayaka'
import { ResourceTopPrice } from './constants'
import { AllRoomOrders, DealOpts, OrderType } from './types'

export default class MarketManager {
    /**
     * 更新时间
     */
    public static updateInterval = 100

    /**
     * 订单最高价缓存，resourceKey 为 `${resourceType}/${type}`
     */
    public static topPriceCache: { [resourceKey: string]: number } = {}

    /**
     * 运行
     */
    public static run() {
        // 调整订单价格
        MarketManager.changeOrderPrice()
        // 自动出售像素
        MarketManager.autoSellPixel()
        // 自动加价收购
        MarketManager.autoRaisePrice()
    }

    /**
     * 定期调整所有订单价格
     */
    public static changeOrderPrice() {
        if (!isInterval(MarketManager.updateInterval)) return

        Object.values(Game.market.orders).forEach((order) => {
            if (order.remainingAmount <= 0) {
                Game.market.cancelOrder(order.id)
                return
            }

            if (!order.active || order.type !== ORDER_BUY || order.amount <= 0 || order.price <= 1) return

            // 全局资源不管
            if (!order.roomName) return

            const newPrice = MarketManager.calcResourcePrice(order.resourceType, order.type, order.roomName)
            if (newPrice > order.price) {
                Game.market.changeOrderPrice(order.id, newPrice)
            }
        })
    }

    /**
     * 查询指定房间指定资源的订单，不同房间应同时查询，这样可以利用缓存
     */
    public static queryMyOrders(roomName: string, resourceType: MarketResourceConstant) {
        if (Game._allRoomOrders && Game._allRoomOrders[roomName]) {
            return Game._allRoomOrders[roomName][resourceType]
        }

        const allRoomOrders = (Game._allRoomOrders = {}) as AllRoomOrders

        Object.values(Game.market.orders).forEach((order) => {
            const roomName = order.roomName || 'global'
            const resourceType = order.resourceType
            if (!allRoomOrders[roomName]) {
                allRoomOrders[roomName] = {}
            }
            if (!allRoomOrders[roomName][resourceType]) {
                allRoomOrders[roomName][resourceType] = []
            }
            allRoomOrders[roomName][resourceType]!.push(order)
        })

        return allRoomOrders[roomName]?.[resourceType]
    }

    /**
     * 计算出价倍率
     */
    public static calcPriceMultiple(resourceType: MarketResourceConstant) {
        // 资源的价格越高，倍率也要越大
        const topPrice = ResourceTopPrice[resourceType]
        // 价格低的是基础资源，要保证基础资源的供应
        if (!topPrice || topPrice <= 30) return 0.985
        return 1 - Math.pow(Math.E, -Math.pow(topPrice, 0.3))
    }

    /**
     * 获取资源近期的平均价格
     */
    public static getResourceAvgPrice = cached((resourceType: MarketResourceConstant) => {
        const history = Game.market.getHistory(resourceType)
        if (!history.length) return 0
        return history.reduce((sum, h) => sum + h.avgPrice, 0) / history.length
    })

    /**
     * 获取市场上指定资源类型订单的最高价（合理的订单，不包含自己）
     */
    public static getTopPrice(resourceType: MarketResourceConstant, type: OrderType = 'buy') {
        // 刷新缓存
        if (isInterval(MarketManager.updateInterval)) MarketManager.topPriceCache = {}

        const key = `${resourceType}/${type}`
        if (MarketManager.topPriceCache[key]) return MarketManager.topPriceCache[key]

        const avgPrice = MarketManager.getResourceAvgPrice(resourceType)
        const orders = Game.market.getAllOrders({ type, resourceType }).filter((order) => {
            // 过滤掉自己的订单
            if (Game.market.orders[order.id]) return false
            // 离谱的价格不管（高于均价 3倍）
            if (order.price > avgPrice * 3) return false
            // 数量过低的不管
            if (order.price <= 500 && order.remainingAmount < 1000) return false

            return true
        })
        // 没有的话按照资源的最高限价的一半计算
        if (orders.length <= 0) return ResourceTopPrice[resourceType] / 2

        const topOrder = _.max(orders, (order) => order.price)
        return (MarketManager.topPriceCache[key] = topOrder.price)
    }

    /**
     * 计算资源出价
     */
    public static calcResourcePrice(resourceType: MarketResourceConstant, type: OrderType = 'buy', roomName?: string) {
        const topPrice = MarketManager.getTopPrice(resourceType, type)
        const priceMultiple = MarketManager.calcPriceMultiple(resourceType)
        let price = Math.min(topPrice * priceMultiple, ResourceTopPrice[resourceType])
        if (roomName) {
            // 防止所有房间一个价被人看出来
            const [x, y] = roomNameToXY(roomName)
            price += (x + y) * 0.001
        }
        // 不要低于 1，低于 1 的订单不会被自动管理
        return Math.max(price, 1.001)
    }

    /**
     * 发布订单
     */
    public static publishOrder(
        roomName: string | undefined,
        resourceType: MarketResourceConstant,
        amount: number,
        type: OrderType = 'buy',
        price?: number,
    ) {
        price = price || MarketManager.calcResourcePrice(resourceType, type, roomName)
        const result = Game.market.createOrder({
            type,
            resourceType,
            price,
            totalAmount: amount,
            roomName,
        })
        return result
    }

    /**
     * 主动 deal
     *
     * @returns [dealResult, dealAmount]
     */
    public static deal(
        roomName: string,
        resourceType: MarketResourceConstant,
        type: OrderType,
        amount: number,
        opts?: DealOpts,
    ): [result: number, realAmount: number] {
        const orders = Game.market.getAllOrders({ type, resourceType }).filter((order) => {
            if (order.roomName && opts?.range && Game.map.getRoomLinearDistance(roomName, order.roomName) > opts.range)
                return false
            if (order.amount < amount / 100) return false
            if (order.type === ORDER_BUY && opts?.minPrice && order.price < opts.minPrice) return false
            if (order.type === ORDER_SELL && opts?.maxPrice && order.price > opts.maxPrice) return false
            if (opts?.minAmount && order.amount < opts.minAmount) return false

            return true
        })

        if (!orders.length) return [ERR_NOT_FOUND, 0]

        const order = type === ORDER_BUY ? _.max(orders, (order) => order.price) : _.min(orders, (order) => order.price)
        const dealAmount = Math.min(amount, order.amount)
        const result = Game.market.deal(order.id, dealAmount, order.roomName ? roomName : undefined)

        return [result, dealAmount]
    }

    /**
     * 购买指定数量资源
     */
    public static autoBuy(roomName: string, resourceType: MarketResourceConstant, amount: number, price?: number) {
        if (Game.market.credits < 1e7) return
        const orders = MarketManager.queryMyOrders(roomName, resourceType)?.filter((order) => order.price > 1)

        if (!orders || !orders.length) {
            MarketManager.publishOrder(roomName, resourceType, amount, ORDER_BUY, price)
        } else {
            const order = orders[0]
            if (order.remainingAmount < amount) {
                Game.market.extendOrder(order.id, amount - order.remainingAmount)
            }
        }
    }

    /**
     * 自动出售像素
     */
    public static autoSellPixel() {
        if (!isInterval(10000)) return

        if (Game.market.credits > 1e7 || Game.resources['pixel'] <= 10) return

        MarketManager.deal('', 'pixel', ORDER_BUY, Game.resources['pixel'], { minAmount: 10 })
    }

    /**
     * 资源自动加价收购
     */
    public static autoRaisePrice() {
        const autoRaiseTypes = new Set(Memory.market?.autoRaiseTypes || [])
        if (!autoRaiseTypes.size) return

        Object.values(Game.market.orders).forEach((order) => {
            if (!autoRaiseTypes.has(order.resourceType)) return
            if (order.type !== ORDER_BUY || order.remainingAmount <= 0) return

            const addPrice = Memory.market?.addPrice || 0.002
            Game.market.changeOrderPrice(order.id, order.price + addPrice)
        })
    }
}

export const updateMarket: LifeCycles = {
    tickStart: MarketManager.run,
}
